/**
 * 
 */
package com.laozhao.deliveryManager.test;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.EventListenerList;

/**
 * @author dozhao
 * @version create time：Mar 7, 2012 11:04:43 AM
 * 
 */
/**
 * @author dozhao
 * 
 */
public class MineGame  {
	private JButton startButton = new JButton("start");
	private MineBox mineBox;

	// private JButton

	public void MineGame() {
//		JFrame frame = new JFrame();
//		JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
//		mainPanel.add(startButton, BorderLayout.NORTH);
//		mineBox = new MineBox(8, 8, 10);
//		mainPanel.add(mineBox, BorderLayout.CENTER);
//		// init view
//		frame.getContentPane().add(mainPanel);
//
//		frame.setSize(400, 500);
//		mineBox.addMyListener(new MineListener() {
//
//			@Override
//			public void rightButtonClick(MineEvent event) {
//				mineBox.openMine(event.getLocation());
//			}
//
//			@Override
//			public void leftButtonClick(MineEvent event) {
//				mineBox.flagMine(event.getLocation());
//			}
//		});
//
//		frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
////		frame.setVisible(true);
//		frame.show();
	}
	public void start() {
		JFrame frame = new JFrame();
		JPanel mainPanel = new JPanel(new BorderLayout(5, 5));
		mainPanel.add(startButton, BorderLayout.NORTH);
		mineBox = new MineBox(8, 8, 10);
		mainPanel.add(mineBox, BorderLayout.CENTER);
		// init view
		frame.getContentPane().add(mainPanel);

		frame.setSize(400, 500);
		mineBox.addMyListener(new MineListener() {

			@Override
			public void rightButtonClick(MineEvent event) {
				mineBox.flagMine(event.getLocation());
			}

			@Override
			public void leftButtonClick(MineEvent event) {
				mineBox.openMine(event.getLocation());
			}
		});

		frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
		frame.setVisible(true);
//		frame.show();
	}

	/**
	 * @param args
	 * @throws UnsupportedLookAndFeelException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
		UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName());
		MineGame game = new MineGame();
//		frame1.setSize(new Dimension(400, 350));
//		frame1.show();
		game.start();

	}


	class MineBox extends JPanel {
		/**
		 * 保存注册的监听器列表. 子类可以使用它保存自己的事件监听器(非MyListener监听器)列表.
		 */
		protected EventListenerList listenerList = new EventListenerList();
		private MineEvent myEvent = null;// fireDoMyAction()使用此变量

		private int row;
		private int column;
		private int mineNum;
		private int canBlowNum;
		private Mine[][] mines;

		public MineBox(int row, int column, int mineNum) {
			this.row = row;
			this.column = column;
			this.mineNum = mineNum;
			mines = new Mine[row][column];
			canBlowNum = row * column;
			initBox();
		}

		public void addListener(MouseListener listener) {
			for (int i = 0; i < row; i++) {
				for (int j = 0; j < column; j++) {
					if (mines[i][j] != null) {
						mines[i][j].addMouseListener(listener);
					}
				}
			}
		}

		private void initBox() {
			setLayout(new GridLayout(row, column, 2, 2));
			// get the blow
			Random random = new Random();
			HashSet<MineLocation> mineSet = new HashSet<MineLocation>();
			while (mineSet.size() < mineNum) {
				int x = random.nextInt(row + 1);
				int y = random.nextInt(column + 1);
				MineLocation mineLocation = new MineLocation(x, y);
				if (!mineSet.contains(mineLocation)) {
					mineSet.add(mineLocation);
				}
			}
			// set the mine box
			Mine mine;
			for (int i = 0; i < row; i++) {
				for (int j = 0; j < column; j++) {
					final MineLocation mineLocation = new MineLocation(i, j);
					if (mineSet.contains(mineLocation)) {
//						mine = Mine.generalMine(true);
						mine = new Mine(true);
					} else {
//						mine = Mine.generalMine(false);
						mine = new Mine(false);
					}
					mine.addMouseListener(new MouseAdapter() {
						@Override
						public void mouseClicked(MouseEvent e) {
							// mouse right button click
							if (e.isMetaDown()) {
								fireRightMineAction(mineLocation, e);
							} else {
								fireLeftMineAction(mineLocation, e);
							}
						}
					});
					mines[i][j] = mine;
					this.add(mine);
				}
			}
			// set the mine mum
			for (int i = 0; i < row; i++) {
				for (int j = 0; j < column; j++) {
					MineLocation location = new MineLocation(i, j);
					mine = getMine(location);
					int num = checkAround(location);
					mine.setRoundNum(num);
				}
			}
		}

		public void openMine(MineLocation location) {
			// mine
			Mine mine = getMine(location);
			if (!mine.blowMine()) {
				openAround(location);
			} else {
				openAllMine();
			}
		}

		public void flagMine(MineLocation location) {
			// mine
			Mine mine = getMine(location);
			mine.flagMine();
		}

		private void openAround(MineLocation location) {
			Mine mine;
			for (int i = 0; i < location.getX(); i++) {
				for (int j = 0; j < location.getY(); j++) {
					if (i != location.getX() && j != location.getY()) {
						mine = getMine(location);
						if (!mine.isMine && !mine.isBlowed()) {
							if (mine.getRoundNum() > 0) {
								mine.blowMine();
							} else {
								openAround(location);
							}
						}
					}
				}
			}
		}

		private void openAllMine() {
			for (int i = 0; i < row; i++) {
				for (int j = 0; j < column; j++) {
					Mine mine = mines[i][j];
					mine.showMine();
				}
			}
		}

		private Mine getMine(MineLocation location) {
			if (location.getX() >= 0 && location.getX() <= row
					&& location.getY() >= 0 && location.getY() <= column) {
				return mines[location.getX()][location.getY()];
			}
			return null;
		}

		private int checkAround(MineLocation location) {
			int mineCount = 0;
			for (int i = 0; i < location.getX(); i++) {
				for (int j = 0; j < location.getY(); j++) {
					if (i != location.getX() && j != location.getY()) {
						mineCount++;
					}
				}
			}
			return mineCount;
		}

		/**
		 * 添加一个MyListener监听器
		 */
		public void addMyListener(MineListener listener) {
			listenerList.add(MineListener.class, listener);
		}

		/**
		 * 移除一个已注册的MyListener监听器. 如果监听器列表中已有相同的监听器listener1、listener2,
		 * 并且listener1==listener2, 那么只移除最近注册的一个监听器。
		 */
		public void removeMyListener(MineListener listener) {
			listenerList.remove(MineListener.class, listener);
		}

		/**
		 * @return 在此对象上监听的所有MyListener类型的监听器
		 */
		public MineListener[] getMyListeners() {
			return (MineListener[]) listenerList
					.getListeners(MineListener.class);
		}

		// Winty:Copy directly from javax.swing.event.EventListenerList
		/*
		 * Notify all listeners that have registered interest for notification
		 * on this event type. The event instance is lazily created using the
		 * parameters passed into the fire method.
		 */
		protected void fireLeftMineAction(MineLocation location,
				MouseEvent event) {
			// getListenerList() Guaranteed to return a non-null array
			Object[] listeners = listenerList.getListenerList();
			// Process the listeners last to first, notifying
			// those that are interested in this event
			for (int i = listeners.length - 2; i >= 0; i -= 2) {
				if (listeners[i] == MineListener.class) {
					// Lazily create the event:
					if (myEvent == null)
						myEvent = new MineEvent(location, event);
					((MineListener) listeners[i + 1]).leftButtonClick(myEvent);
				}
			}
		}

		protected void fireRightMineAction(MineLocation location,
				MouseEvent event) {
			// getListenerList() Guaranteed to return a non-null array
			Object[] listeners = listenerList.getListenerList();
			// Process the listeners last to first, notifying
			// those that are interested in this event
			for (int i = listeners.length - 2; i >= 0; i -= 2) {
				if (listeners[i] == MineListener.class) {
					// Lazily create the event:
					if (myEvent == null)
						myEvent = new MineEvent(location, event);
					((MineListener) listeners[i + 1]).rightButtonClick(myEvent);
				}
			}
		}

	}

	class MineLocation {
		private int x;
		private int y;

		public MineLocation() {

		}

		public MineLocation(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public int getX() {
			return x;
		}

		public int getY() {
			return y;
		}
	}
	public enum MineStatus {
		flag, nature, blow
	}
	class Mine extends JButton {
		

		private MineStatus status;
		private final boolean isMine;
		private int roundNum;

//		public static Mine generalMine(Boolean isMine) {
//			if (isMine == null) {
//				throw new IllegalArgumentException();
//			}
//			Mine mine = new Mine(isMine);
//			return mine;
//		}

		public Mine(boolean isMine) {
			this.isMine = isMine;
			setText(" ");
			status = MineStatus.nature;
			setSize(5, 5);
		}

		public void showMine() {
			switch (status) {
			case flag:
				if (!isMine) {
					setText("☆");
				}
				break;
			case nature:
				if (isMine) {
					setText("◆");
				} else {
					setText(getRoundString());
				}
				break;
			}
			status = MineStatus.blow;
			setEnabled(false);
		}

		public boolean blowMine() {
			switch (status) {
			case flag:
				setText(" ");
				status = MineStatus.nature;
				break;
			case nature:
				if (isMine) {
					setText("◆");
				} else {
					setText(getRoundString());
				}
				status = MineStatus.blow;
				setEnabled(false);
				break;
			}
			return isMine;
		}

		public boolean flagMine() {
			switch (status) {
			case flag:
				setText(" ");
				status = MineStatus.nature;
				break;

			case nature:
				setText("★");
				status = MineStatus.flag;
				break;
			}
			return isMine;
		}

		public boolean isMine() {
			return isMine;
		}

		public boolean isBlowed() {
			if (status == MineStatus.blow || status == MineStatus.flag) {
				return true;
			} else {
				return false;
			}
		}

		public String getRoundString() {
			if (roundNum == 0) {
				return " ";
			} else {
				return String.valueOf(roundNum);
			}
		}

		public void setRoundNum(int roundNum) {
			this.roundNum = roundNum;
		}

		public int getRoundNum() {
			return roundNum;
		}
	}

	/**
	 * GAME LEVEL
	 * 
	 * @author dozhao
	 * 
	 */
	public enum GameLevelEnum {
		low(10), middle(20), hight(50), customer(100);

		private int mineNum;

		public void setMineNum(int mineNum) throws IllegalArgumentException {
			if (this.equals(GameLevelEnum.customer)) {
				this.mineNum = mineNum;
			} else {
				throw new IllegalArgumentException();
			}
		}

		public int getMineNum() {
			return mineNum;
		}

		GameLevelEnum(int mineNum) {
			this.mineNum = mineNum;
		}
	}

	public interface MineListener extends java.util.EventListener {
		void rightButtonClick(MineEvent event);

		void leftButtonClick(MineEvent event);
	}

	public class MineEvent extends MouseEvent {
		protected MineLocation location;

		public MineEvent(Component source, int id, long when, int modifiers,
				int x, int y, int clickCount, boolean popupTrigger) {
			super(source, id, when, modifiers, x, y, clickCount, popupTrigger);
			// TODO Auto-generated constructor stub
		}

		public MineEvent(MineLocation location, MouseEvent mouseEvent) {
			super((Component) mouseEvent.getSource(), mouseEvent.getID(),
					mouseEvent.getWhen(), mouseEvent.getModifiers(), mouseEvent
							.getX(), mouseEvent.getY(), mouseEvent
							.getClickCount(), false);
			this.location = location;
		}

		public MineLocation getLocation() {
			return location;
		}

		public void setLocation(MineLocation location) {
			this.location = location;
		}
	}
}
